/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.kiakaku.downloadpage;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.htmlparser.jericho.*;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleDirectedGraph;
import org.kiakaku.utility.FileUtility;

/*
 *   File OutLink is created by kiakaku on Mar 2, 2012 1:24:57 PM
 * If you want modify this file please insert information in here 
 * - Last modify in Mar 2, 2012, 1:24:57 PM by kiakaku
 *                  
 *
 * @author kiakaku
 */
public class OutLinkold1 {

    public static final String SOURCE_FILE = "source_list.txt";
    public static final String TAGET_FILE = "target_list.txt";
    public static final int MAX_PAGE = 60000;
    String firstURL;
    SimpleDirectedGraph<Node, DefaultEdge> graph;
    private static int numberOfPage = 0; // so luong url da xet
    List<Node> datungtrongqueue; // luu tru toan bo url 
    Queue<Node> queue;
    TextExtractor txtExtract;
    int id;
    List<String> getOuted; // url đã xét outlink rồi

    public OutLinkold1() {
        id = 1;
        firstURL = "http://dantri.com.vn";
        graph = new SimpleDirectedGraph<Node, DefaultEdge>(DefaultEdge.class);
        datungtrongqueue = new ArrayList<Node>();
        queue = new LinkedList<Node>();
        getOuted = new ArrayList<String>(); // lưu trữ các node đã lưu trữ nội dung rồi
        MicrosoftConditionalCommentTagTypes.register();
        PHPTagTypes.register();
        PHPTagTypes.PHP_SHORT.deregister(); // remove PHP short tags for this example otherwise they override processing instructions
        MasonTagTypes.register();
    }

    public boolean contentIsSaved(String url) {
        return getOuted.contains(url);
    }

    /*
     * Hàm lấy ra tất cả các link chưa trong trang web mà có url là tham số đầu vào
     * Và chú ý là chỉ lấy các phần tử nằm trong element <div class="wrapper"> </div>
     * Paramater : url (String) url của trang web cần lấy outlink
     * return : List<String> danh sách địa chỉ các trang chứa trong trang url
     */
    public List<Node> getOutNode(Node currNode) {
        List<Node> outlink = new ArrayList<Node>();
        try {
            Source source = new Source(new URL(currNode.getUrl()));
            source.fullSequentialParse();
            List<Element> div = source.getAllElements(HTMLElementName.DIV);
            Element wrapper = null;
            for (Element temp : div) {
                if ("wrapper".equalsIgnoreCase(temp.getAttributeValue("class"))) {
                    wrapper = temp;
                    break;
                }
            }
            if (wrapper != null) {
                List<Element> linkElements = wrapper.getAllElements(HTMLElementName.A);
                for (Element linkElement : linkElements) {
                    String href = linkElement.getAttributeValue("href");
                    if (href == null || href.length() == 1) {
                        continue;
                    }
                    if (getMainURL(href) == null) { // nếu đây là địa chỉ tương đối
                        if (href.charAt(0) != '/') {
                            href = getMainURL(currNode.getUrl()) + "/" + href;
                        }
                        href = getMainURL(currNode.getUrl()) + href;
                    }
                    if (href.indexOf('?') != -1 // loại bỏ các trang có dấu ?
                            || !".htm".equalsIgnoreCase(href.substring(href.length() - 4)) // chỉ lấy các trang có phần cuối là .htm
                            || href.indexOf('#') != -1
                            || href.lastIndexOf(':') > 4 // loại bỏ url có dấu : ngoại trừ : trong http:
                            || href.contains("http://m.") || href.contains("https://m.") // loại bỏ các trang cho điện thoại di động
                            || href.contains("javascript")
                            || href.equalsIgnoreCase(currNode.getUrl())
                            || currNode.getUrl().equalsIgnoreCase(href.substring(0, href.length() - 1))) { // Nếu trùng với url đầu vào thì bỏ qua
                        continue;
                    }
                    //TODO: Đoạn if này là để mỗi mức chỉ lấy một số lượng site là 2 để test
//                if (outlink.size() >= 2) {
//                    break;
//                }

                    if (!(outlink.contains(new Node(href, -1, -1)))) {
                        if (href.contains("http://dantri.com.vn")) {
                            outlink.add(new Node(href, currNode.getDepth() + 1, -1)); // rang của node được tăng lên một
                        }
                    }
                }
            }
        } catch (Exception ex) {
            Logger.getLogger(OutLink.class.getName()).log(Level.SEVERE, null, ex);
        }
        return outlink;
    }
    /*
     * Hàm để add một tập các phần tử vào trong queue
     * Chỉ add các phần từ mà chưa từng xuất hiện trong queue
     */

    public void addListToQueue(Queue<Node> a, List<Node> b) {
        for (int i = 0; i < b.size(); i++) {
            if (!(datungtrongqueue.contains(b.get(i)))) {
                a.offer(b.get(i));
                datungtrongqueue.add(b.get(i));
            }
        }
    }
    /*
     * Tìm xem Node có url đã có trong đồ thì chưa 
     * Nếu có thì trả về Node đó
     * Nếu chưa thì trả về null
     */

    public Node findNode(Graph gr, String url) {
        List<Node> temp = new ArrayList<Node>(gr.vertexSet());
        Node a = new Node(url, -1, -1);
        int i = temp.indexOf(a);
        if (i < 0) {
            return null;
        }
        return temp.get(i);
    }

    public void breadthFirstSearch(String root) {
        numberOfPage = 0;
        // khai báo tránh vòng lặp
        List<Node> temp = new ArrayList<Node>();
        Node curr = new Node(root, 0, 0);
        queue.offer(curr);
        curr.setId(numberOfPage);
        graph.addVertex(curr);
        numberOfPage++;
        Node next = null;
        while (!queue.isEmpty() && numberOfPage <= MAX_PAGE) { // duyệt theo chiều rộng đến khi đạt được số lượng web là MAX_PAGE
            curr = queue.poll();
            temp = getOutNode(curr);
            addListToQueue(queue, temp);// push toàn bộ node kề với node curr
            for (int i = 0; i < temp.size(); i++) { // Với mỗi đỉnh next kề với node curr
                if (numberOfPage > MAX_PAGE) {
                    break;
                }
                next = temp.get(i);
                if (!(graph.containsVertex(next))) {
                    next.setId(numberOfPage);
                    graph.addVertex(next);
                    System.out.println("\t\t\t\tTrang số :" + numberOfPage);
                    numberOfPage++;
                    graph.addEdge(curr, next);
                } else { // trong đồ thì đã có node next
                    if (curr.getDepth() <= next.getDepth()) {
                        graph.addEdge(curr, next);
                    }
                }
            }
        }
        List<Node> source = new ArrayList<Node>();
        List<Node> target = new ArrayList<Node>();
        List<DefaultEdge> edgeList = new ArrayList<DefaultEdge>(graph.edgeSet());
        for (DefaultEdge defaultEdge : edgeList) {
            source.add(graph.getEdgeSource(defaultEdge));
            target.add(graph.getEdgeTarget(defaultEdge));
        }
        FileUtility.ghiListNode(source, SOURCE_FILE);
        FileUtility.ghiListNode(target, TAGET_FILE);
    }

    public int getNumberOfSize() {
        return numberOfPage;
    }

    /*
     * Hàm để lấy tên miền của trang web hiện tài
     * Ví dụ : http://dantri.com.vn/c20/s20-570788/ha-noi-xe-lexus-bac-ty-bat-ngo-boc-chay.htm
     * thì kết quả trả về sẽ là http://dantri.com.vn
     * Nếu url hiện tại ko có tên miền chính
     * Ví dụ : /c20/s20-570788/ha-noi-xe-lexus-bac-ty-bat-ngo-boc-chay.htm
     * Thì kết quả trả về null
     * 
     * Hàm đã test chuẩn
     * Kiakaku
     */
    public String getMainURL(String url) {
        if (url.length() < 7) {
            return null;
        } else {
            if ("http://".equalsIgnoreCase(url.substring(0, 7))) {
                int pos = url.substring(7).indexOf('/');
                if (pos == -1) {
                    return url;
                }
                return url.substring(0, 7 + pos);
            }
        }
        return null;
    }

    public boolean downloadSite(Node site) {
        try {
            Source sourceTemp = new Source(new URL(site.getUrl()));
            txtExtract = new TextExtractor(sourceTemp) {

                @Override
                public boolean excludeElement(StartTag st) {
                    return (st.getName() == null ? HTMLElementName.INPUT == null : st.getName().equals(HTMLElementName.INPUT))
                            || (st.getName() == null ? HTMLElementName.A == null : st.getName().equals(HTMLElementName.A))
                            || "footer box19".equalsIgnoreCase(st.getAttributeValue("class"))
                            || (st.getName() == null ? HTMLElementName.META == null : st.getName().equals(HTMLElementName.META))
                            || "/zone-26/event.htm".equalsIgnoreCase(st.getAttributeValue("href"))
                            || "javascript:;".equalsIgnoreCase(st.getAttributeValue("href"))
                            || "comment".equalsIgnoreCase(st.getAttributeValue("id"))
                            || "commentform".equalsIgnoreCase(st.getAttributeValue("id"))
                            || "respond".equalsIgnoreCase(st.getAttributeValue("id"))
                            || "widget".equalsIgnoreCase(st.getAttributeValue("class"))
                            || "footer".equalsIgnoreCase(st.getAttributeValue("id"));

                }
            };
            FileUtility.ghiFile(site.getUrl() + "\n" + txtExtract.toString(), "data\\" + site.getId() + ".data");
            txtExtract = null;
            return true;
        } catch (IOException ex) {
            Logger.getLogger(OutLink.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            return false;
        }
    }
    /*
     * Thêm node phụ là node nối đất và duyệt đồ thì để xây dựng hệ phương trình 
     */

    public void duyetDoThi() {
    }

    public void writeGraphToFile(String path) {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(path));
            List<Node> a = new ArrayList<Node>(graph.vertexSet());
            graph.edgeSet();
        } catch (IOException ex) {
            Logger.getLogger(OutLink.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (oos != null) {
                    oos.flush();
                    oos.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        OutLink a = new OutLink();
        a.breadthFirstSearch("http://dantri.com.vn/c26/s26-574831/cuoc-dua-giua-man-city--man-utd-30-chua-phai-latet.htm");
        System.out.println("Kích thước đồ thị :" + a.graph.vertexSet().size());
    }
}
